#include "boost/tuple/tuple.hpp"
#include <iostream>
#include <algorithm>
#include <vector>
#include <ext/hash_map>
#include <istream>
#include <map>
#include <deque>
#include <cassert>

using std::istream;
using std::vector;
using std::string;
using std::cin;
using std::cout;
using std::cerr;
using std::map;
using std::deque;
//using std::assert;

struct eqstr
{
	bool operator()(const char* s1, const char* s2) const
	{	return strcmp(s1, s2) == 0;	}
};



typedef __gnu_cxx::hash_map<const char*, unsigned,
							__gnu_cxx::hash<const char*>, eqstr> t_map_str_uint;

unsigned lookup_gene_index(	const string&		name,
							t_map_str_uint&		gene_name_to_gi,
							deque<string>&		gi_to_gene_name,
							deque<unsigned>&	gi_to_counts,
							vector<unsigned>&	gi_to_cluster)
{
	// if name is already there
	t_map_str_uint::iterator p_index = gene_name_to_gi.find(name.c_str());
	if (p_index != gene_name_to_gi.end())
	{
		return p_index->second;
	}

	// name is new
	unsigned gene_index = gi_to_gene_name.size();
	gi_to_gene_name.push_back(name);
	gi_to_counts.push_back(0);
	gi_to_cluster.push_back(0);
	gene_name_to_gi[gi_to_gene_name.back().c_str()] = gene_index;
	return gene_index;
}

void cluster_from_pairs(istream& 					is,
						vector<vector<unsigned> >&	final_gene_clusters,
					   	deque<string>&				gi_to_gene_name,
						deque<unsigned>&			gi_to_counts)
{
	vector<unsigned>	gi_to_cluster; 		// which cluster a particular gene belongs to
	t_map_str_uint		gene_name_to_gi;
	gene_name_to_gi.resize(500000);
	vector<vector<unsigned> > gene_clusters(1);	// first cluster is empty: dummy flag value
	
	string gene_name1, gene_name2;
	is >> gene_name1 >> gene_name2;
	unsigned line_cnt = 0;
	while (is)
	{
		if ((++line_cnt % 15000) == 0)
		    cerr << ".";
		unsigned gi1 = lookup_gene_index(	gene_name1, gene_name_to_gi,
					   						gi_to_gene_name, gi_to_counts, gi_to_cluster);
		unsigned gi2 = lookup_gene_index(	gene_name2, gene_name_to_gi,
					   						gi_to_gene_name, gi_to_counts, gi_to_cluster);
		if (gi1 == gi2)
		{
			is >> gene_name1 >> gene_name2;
			continue;
		}

		++gi_to_counts[gi1];
		++gi_to_counts[gi2];
		unsigned clustid1 = gi_to_cluster[gi1];
		unsigned clustid2 = gi_to_cluster[gi2];

		//
		// add new cluster if neither is yet clustered
		//
		if (clustid1 + clustid2 == 0)
		{
			// cerr << "join " << gene_name1 << ":" << gi1 << "," << gene_name2 << ":" << gi2 << " orig " << clustid1 << ","
			//					<< clustid2 << std::endl;
			
			// new cluster index for these two genes
			gi_to_cluster[gi1] =
			gi_to_cluster[gi2] = gene_clusters.size();
			
			
			// these two genes make up a new cluster
			gene_clusters.push_back(vector<unsigned>());
			gene_clusters.back().push_back(gi1);
			gene_clusters.back().push_back(gi2);
			assert(gi1 != gi2);
			assert(	gene_clusters[gi_to_cluster[gi2]][0] !=
					gene_clusters[gi_to_cluster[gi2]][1]);
			// cerr << "join " << gi1 << "," << gi2 << " at " << gi_to_cluster[gi1] << ","
			//					<< gi_to_cluster[gi2] << std::endl;
		}

		//
		// link cluster 2 to cluster 1 if both already have clusters
		//
		else if (clustid1 && clustid2)
		{
			//cerr << "clust2 " << clustid2 << " >> clust1 " <<clustid1 << "\n";
			// ignore if they already belong to the same cluster
			if (clustid1 == clustid2)
			{
				is >> gene_name1 >> gene_name2;
				continue;
			}


			// assign clustid1 to all of clustid2 genes;
			{
				vector<unsigned>::iterator i	= gene_clusters[clustid2].begin();
				vector<unsigned>::iterator end	= gene_clusters[clustid2].end();
				while (i != end)
					gi_to_cluster[*i++] = clustid1;
			}	


			// merge cluster 2 into cluster 1
			gene_clusters[clustid1].insert(	gene_clusters[clustid1].end(),
											gene_clusters[clustid2].begin(),
											gene_clusters[clustid2].end());
			assert(	gene_clusters[clustid1][0] !=
					gene_clusters[clustid1][1]);
			
			// cluster 2 holds nothing now
			std::vector<unsigned>().swap(gene_clusters[clustid2]);
			assert(	gene_clusters[clustid2].size() == 0);
		}
		
		//
		// add gene 2 to cluster 1 if gene 2 is unclustered
		//
		else if (clustid1)
		{
			// cerr << "Add " << gi2 << "to cluster " << clustid1 << " of first " << gi1 << "\n";
			gi_to_cluster[gi2] = clustid1;
			gene_clusters[clustid1].push_back(gi2);
			assert(	gene_clusters[clustid1][0] !=
					gene_clusters[clustid1][1]);
		}
	
		//
		// add gene 1 to cluster 2 if gene 1 is unclustered
		//
		else // if (clustid2)
		{
			// cerr << "Add " << gi1 << "to cluster " << clustid2 << " of second " << gi2 << "\n";
			gi_to_cluster[gi1] = clustid2;
			gene_clusters[clustid2].push_back(gi1);
			assert(	gene_clusters[clustid2][0] !=
					gene_clusters[clustid2][1]);
		}

		is >> gene_name1 >> gene_name2;
	}
	
	unsigned cnt_genes = 0;
	for (unsigned i = 0; i < gene_clusters.size(); ++i)
	{
		if (!gene_clusters[i].size())
			continue;
		cnt_genes += gene_clusters[i].size();
		final_gene_clusters.push_back(vector<unsigned>());
		final_gene_clusters.back().swap(gene_clusters[i]);
	}
	
	//return final_gene_clusters;
}

struct sort_by_names
{
	deque<string>&		gi_to_gene_name;
	sort_by_names(deque<string>& g)
		: gi_to_gene_name(g){}
	bool operator () ( unsigned a, unsigned b) const
	{
		return gi_to_gene_name[a] < gi_to_gene_name[b];
	}
};

int main(int argc, char* argv[])
{
	std::vector<std::vector<unsigned> > final_gene_clusters;
	deque<string>		gi_to_gene_name;
	deque<unsigned>		gi_to_counts;
	cluster_from_pairs(cin, final_gene_clusters, gi_to_gene_name, gi_to_counts);
	std::map<unsigned, unsigned> cluster_sizes;
	for (unsigned i = 0; i < final_gene_clusters.size(); ++i)
	{
		cluster_sizes[final_gene_clusters[i].size()]++;
		sort_by_names sort_algo(gi_to_gene_name);
		sort(final_gene_clusters[i].begin(), final_gene_clusters[i].end(), sort_algo);
		cout << i << "\n";
		for (unsigned j = 0; j < final_gene_clusters[i].size(); ++j)
			cout << gi_to_gene_name[final_gene_clusters[i][j]] << "\t" <<
					gi_to_counts[final_gene_clusters[i][j]] <<"\n";
		cout << std::endl;
	}
	std::map<unsigned, unsigned>::iterator it, end;
	it = cluster_sizes.begin();
	for (; it != cluster_sizes.end(); ++it)
	    cerr << it->first << " : " << it->second << "\n";
	return 0;
}
